package noname1;

import java.util.PriorityQueue;

import objects.Building;
import objects.Explosion;
import objects.Human;
import objects.Item;
import objects.UnrealObject;
import test.kryonet.network.HasTimeStamp;

import com.badlogic.gdx.math.Circle;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.IntArray;
import com.badlogic.gdx.utils.IntMap;

public class UnrealWorld implements Runnable {

	private final int FRAMES_PER_SECOND = 30;
	private long maxWorkingTimePerFrame = 1000 / FRAMES_PER_SECOND;  
	private long lastStartTime=0;
	private boolean stop=false;
	
	private PriorityQueue<HasTimeStamp> queue;
	private IntMap<UnrealObject> actorMap;
	
	private IntArray humanIndices;
	private IntArray buldingIndices;
	private IntArray explosionIndices;
	private IntArray itemIndices;
	
	private Vector2 mapsize;
	
	public UnrealWorld (RealServer snc) {
		queue = snc.getQueue();
		actorMap = snc.getActorMap();
		humanIndices = new IntArray(16);
		buldingIndices = new IntArray(8);
		explosionIndices = new IntArray(4);
		itemIndices = new IntArray(8);
	}
	
	public void initializeWorld (Vector2 mapsize, Human[] hList, Explosion[] eList, Building[] bList, Item[] iList) {
		this.mapsize = mapsize;
		for ( Human h: hList ) {
			actorMap.put(h.getId(), h);
			humanIndices.add(h.getId());
		}
		for ( Building b: bList ) {
			actorMap.put(b.getId(), b);
			humanIndices.add(b.getId());
		}
		for ( Item i: iList ) {
			actorMap.put(i.getId(), i);
			humanIndices.add(i.getId());
		}
		for ( Explosion e: eList ) {
			actorMap.put(e.getId(), e);
			humanIndices.add(e.getId());
		}
	}
	public void stop() {
		stop = true;
	}
	
	@Override
	public void run() {
		lastStartTime = System.currentTimeMillis();
		while(true && !stop)
		{
		    lastStartTime = System.currentTimeMillis();
		    
		    synchronized (queue) {
		    	doEvents(actorMap, queue );
		    	doCollision(actorMap );
		    	update(actorMap);
		    	generateActors(actorMap);
		    }
		    
		    long processingTimeForCurrentFrame = System.currentTimeMillis() - lastStartTime;
		    if(processingTimeForCurrentFrame  < maxWorkingTimePerFrame)
		    {
		        try
		        {
		            Thread.sleep(maxWorkingTimePerFrame - processingTimeForCurrentFrame);
		        }
		        catch(Exception e)
		        {
		        	e.printStackTrace();
		        }
		    }
		}
	}

	

	private void doEvents(IntMap<UnrealObject> map, PriorityQueue<HasTimeStamp> queue2) {
		
	}

	private void doCollision(IntMap<UnrealObject> map) {
		/*human collides with other humans */
		Human m,n;
		Vector2 pos_1, pos_2, move_1, move_2;
		for ( int i = 0 ; i < humanIndices.size; i++ ) {
			m = (Human)map.get(i);
			pos_1 = m.getPosition();
			move_1 = m.getMoveVector();
			for ( int k = i + 1; k < humanIndices.size; k ++ ) {
				n = (Human) map.get(k);
				pos_2 = n.getPosition();
				move_2 = n.getMoveVector();
				while ( 
						Intersector.overlaps(
						Rectangle.tmp
							.setPosition(pos_1.x + move_1.x, pos_1.y + move_1.y)
							.setSize(m.getBound().x, m.getBound().y), 
						Rectangle.tmp2
							.setPosition(pos_2.x + move_2.x, pos_2.y + move_2.y)
							.setSize(n.getBound().x, n.getBound().y)) 
				) {
					/*collision happens */
					move_1.scl(0.75f);
					move_2.scl(0.75f);
				}
			}
		}
		
		
		/* human collides with explosions */
		Explosion x;
		Vector2 orginCollision;
		for ( int i = 0 ; i < humanIndices.size; i++ ) {
			m = (Human)map.get(i);
			pos_1 = m.getPosition();
			move_1 = m.getMoveVector();
			for ( int k = 0; k < explosionIndices.size; k ++ ) {
				x = (Explosion) map.get(k);
				move_2 = x.getMoveVector();
				while ( 
						Intersector.overlaps(
							x.getBound(),
						Rectangle.tmp
							.setPosition(pos_1.x + move_1.x, pos_1.y + move_1.y)
							.setSize(m.getBound().x, m.getBound().y)) 
				) {
					/* collision happens */
					//orginCollision
				}
			}
		}
	}
	private void update(IntMap<UnrealObject> map) {
		for ( UnrealObject ub : map.values() ) {
			ub.doUpdate();
		}
	}

	private void generateActors(IntMap<UnrealObject> actorMap2) {
		/* w przypadku cos trzeba wygenerowac, np. items lub cokolwiek */
	}
}
